package wsi.exp.exp3.compare;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.rygh.core.algorithm.OrderedPrecisionRecall;
import org.rygh.core.face.IHasName;
import org.rygh.core.html.element.HtmlAnchor;
import org.rygh.core.html.element.HtmlBr;
import org.rygh.core.html.element.HtmlElement;
import org.rygh.core.html.element.HtmlHr;
import org.rygh.core.html.element.HtmlParagraph;
import org.rygh.core.html.element.HtmlWrapper;
import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;

import wsi.exp.Experiment.Color;
import wsi.exp.exp3.GoldSetMatch;
import wsi.exp.exp3.PathMatch;
import wsi.exp.exp3.PathMatchPair;
import wsi.function.Function;

public abstract class Exp3Comparator implements IHasName {
	public static String getAnchor(Function function, int threshold) {
		return String.format("<a href=\"#a%d\">", getName(function, threshold)
				.hashCode());
	}

	public static String getName(Function function, int threshold) {
		return String.format("%s, threshold = %d%%", function.getFullName(),
				threshold);
	}

	private final List<HtmlElement> contextInfo = new ArrayList<>();

	protected final int threshold;

	protected final Function function;
	protected OrderedPrecisionRecall recall;

	protected GoldSetMatch goldSet;
	protected PathMatch matchResult;

	public Exp3Comparator(Function function, int threshold) {
		this.threshold = threshold;
		this.function = function;
	}

	public abstract void calibrate();

	protected abstract boolean checkHit(PathMatchPair pair, boolean gold);

	public double getAveragePrecision() {
		return recall.getAveragePrecisionInPercentage();
	}

	public double getFScore() {
		return recall.getFScoreInPercentage();
	}

	public Function getFunction() {
		return function;
	}

	public int getGoldSize() {
		return recall.getGoldSize();
	}

	public List<HtmlElement> getHtmlElements() {
		List<HtmlElement> list = new ArrayList<>();

		list.add(new HtmlHr());
		list.add(new HtmlWrapper(new HtmlAnchor(getName()).toAnchorString()));
		list.add(new HtmlParagraph("<b>Function:</b> " + function.getService()
				+ "/" + function.getName()));
		list.addAll(contextInfo);

		TableData data = new TableData();
		TableTuple tuple = new TableTuple();

		tuple.add("XPath", "Predicate Path");
		data.add(tuple);

		List<PathMatchPair> pairs = goldSet.getPairs();

		for (PathMatchPair p : pairs) {
			tuple = new TableTuple();
			tuple.add(p.getXmlPath().getValue());
			tuple.add(p.getPredicatePath().getKey());

			if (!checkHit(p, true))
				tuple.setBackgroundColor(Color.red);

			data.add(tuple);
		}

		list.add(new HtmlTable("Gold Set", data));
		list.add(new HtmlBr());

		//

		data = new TableData();
		tuple = new TableTuple();

		tuple.add("XPath", "Predicate Path", "Count", "Total", "Confidence");
		data.add(tuple);

		pairs = matchResult.getPairs();

		for (PathMatchPair p : pairs) {
			tuple = new TableTuple();
			tuple.add(p.getXmlPath().getValue());
			tuple.add(p.getPredicatePath().getKey());
			tuple.add(p.getCount());
			tuple.add(p.getTotal());
			tuple.add(String.format("%.2f%%", 100. * p.getConfidence()));

			if (p.getPredicatePath().isTemp() && !checkHit(p, false))
				tuple.setBackgroundColor(Color.blue);
			else if (!checkHit(p, false))
				tuple.setBackgroundColor(Color.red);

			data.add(tuple);
		}

		data.sort(2);

		list.add(new HtmlTable("Results", data));
		list.add(new HtmlBr());

		return list;
	}

	@Override
	public String getName() {
		return getName(function, threshold);
	}

	public double getPrecision() {
		return recall.getPrecisionInPercentage();
	}

	public double getRecall() {
		return recall.getRecallInPercentage();
	}

	public int getRelevant() {
		return recall.getRelevant();
	}

	public int getRetrieved() {
		return recall.getRetrieved();
	}

	public void setContextInfo(List<HtmlElement> elements) {
		contextInfo.clear();
		contextInfo.addAll(elements);
	}

	public void setGoldSet(GoldSetMatch goldSet) {
		this.goldSet = goldSet;
	}

	public void setMatchResult(PathMatch matchResult) {
		this.matchResult = matchResult;
	}

	protected void sortPairs(List<PathMatchPair> pairs) {
		Collections.sort(pairs, new Comparator<PathMatchPair>() {
			@Override
			public int compare(PathMatchPair o1, PathMatchPair o2) {
				return o2.getCount() - o1.getCount();
			}
		});
	}
}
